package com.smart.common.dao;

import java.io.Serializable;
import java.lang.reflect.Array;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.Dictionary;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.hibernate.Query;
import org.hibernate.criterion.Order;
import org.hibernate.metadata.ClassMetadata;
import org.hibernate.transform.Transformers;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.smart.common.domain.BaseEntity;
import com.smart.common.util.HQHelper;
import com.smart.common.util.Page;
import com.smart.common.util.PageImpl;
import com.smart.common.util.Pageable;

public abstract class AbstractDao<E extends BaseEntity> extends BaseDao<E>{

	Logger logger = LoggerFactory.getLogger(AbstractDao.class);

	private int paramLevel = 0;
	private int listParamIdx = 0;

	protected Class<E> entityClass;

	@SuppressWarnings("unchecked")
	public AbstractDao() {
		Type type = this.getClass().getGenericSuperclass();
		if (!(type instanceof ParameterizedType)) {
			// throw new IllegalArgumentException("没有传递类型参数E");
			return;
		}
		try {
			this.entityClass = (Class<E>) ((ParameterizedType) getClass()
					.getGenericSuperclass()).getActualTypeArguments()[0];
		} catch (Exception e) {
			logger.error("", e);
		}
	}

	/**
	 * 获取所有的注解实体@Entity信息
	 * 
	 * @return
	 */
	public Map<String, ClassMetadata> getAllClassMetadata() {
		return sessionFactory.getAllClassMetadata();
	}

	public E getEntity(Serializable eid) {
		return super.get(entityClass, eid);
	}

	@SuppressWarnings("unchecked")
	public E loadEntity(Serializable eid) {
		return (E) super.load(entityClass, eid);
	}


	public E saveEntity(E e) {
		return super.save(e);
	}

	public E updateEntity(E e) {
		return super.update(e);
	}

	public E saveOrUpdateEntity(E e) {
		return super.saveOrUpdate(e);
	}

	public void deleteEntity(E e) {
		super.delete(e);
	}

	public void logicDeleteEntity(E e) {
		super.logicDelete(e);

	}

	public void deleteEntity(Serializable eid) {
		super.delete(entityClass, eid);
	}

	public void logicDeleteEntity(Serializable eid) {
		super.logicDelete(entityClass, eid);
	}

	public void deleteEntity(Serializable[] eids) {
		super.delete(entityClass, eids);

	}

	public void logicDeleteEntity(Serializable[] eids) {
		super.logicDelete(entityClass, eids);
	}

	public void deleteEntity(List<E> es) {
		for (E e : es)
			super.delete(entityClass, e.getId());
	}

	public void logicDeleteEntity(List<E> es) {
		for (E e : es)
			super.logicDelete(entityClass, e.getId());
	}

	public Long countAllEntity() {
		return super.countAll(entityClass);
	}

	/**
	 * 使用HQL查询获取对象
	 * 
	 * @param hql
	 *            HQL语句
	 * @return 对象集
	 */
	protected List<E> findByHql(final String hql) {
		return findByHql(hql, new Object[] {});
	}

	/**
	 * 使用HQL查询获取对象
	 * 
	 * @param hql
	 *            HQL语句
	 * @param values
	 *            参数值
	 * @return 对象集
	 */
	@SuppressWarnings("unchecked")
	protected List<E> findByHql(final String hql, final Object... values) {
		Query q = this.getSession().createQuery(hql);
		if (values != null) {
			for (int i = 0; i < values.length; i++) {
				q.setParameter(i, values[i]);
			}
		}
		return q.list();
	}

	/**
	 * 使用HQL查询获取对象
	 * 
	 * @param hql
	 *            HQL语句
	 * @param condition
	 *            查询条件
	 * @return 对象集
	 */
	@SuppressWarnings("unchecked")
	protected List<E> findByHql(final String hql,
			final Map<String, Object> conditions) {
		Query q = this.getSession().createQuery(hql);
		q.setProperties(conditions);
		return q.list();
	}

	public Page<E> findAll(Pageable pageable) {
		if (pageable == null) {
			throw new IllegalArgumentException("pageable 不能为空!");
		}

		Page<E> pager = new PageImpl<E>(pageable);
		List<E> records = findAll(entityClass, pageable.getPage(),
				pageable.getRows());

		pager.setRecords(records);
		pager.setTotalRows(super.countAll(entityClass));

		return pager;
	}
	


	/**
	 * 分页查询所有的数据，并根据sort指定的排序项目和排序方式进行排序
	 * 
	 * @param pageable
	 *            分页对象
	 * @param sort
	 *            排序条件，key - 用于排序的Property，value - 排序方式，asc 或者 desc，不区分大小写
	 * @return
	 */
	public Page<E> findAll(Pageable pageable, Map<String, String> sortConditions) {
		if (pageable == null) {
			throw new IllegalArgumentException("pageable 不能为空!");
		}

		Page<E> pager = new PageImpl<E>(pageable);
		HQHelper helper = new HQHelper();

		if (sortConditions != null&&sortConditions.size()>0) {
			for (String sortProperty : sortConditions.keySet()) {
				String sortType = sortConditions.get(sortProperty);

				if (sortType.equalsIgnoreCase("asc")) {
					helper.addOrder(Order.asc(sortProperty));
				}

				if (sortType.equalsIgnoreCase("desc")) {
					helper.addOrder(Order.desc(sortProperty));
				}
			}
		}
		List<E> records = findAll(entityClass, helper, pageable.getPage(),
				pageable.getRows());

		pager.setRecords(records);
		pager.setTotalRows(super.countAll(entityClass));

		return pager;
	}

	protected Page<E> findByHql(Pageable pageable, String hql) {
		return findByHql(pageable, hql, Collections.emptyMap());
	}

	@SuppressWarnings("unchecked")
	protected Page<E> findByHql(Pageable pageable, String hql, Object... values) {
		if (pageable == null) {
			throw new IllegalArgumentException("pageable 不能为空!");
		}
		Page<E> pager = new PageImpl<E>(pageable);

		Query q = this.getSession().createQuery(hql);
		q.setFirstResult((pageable.getPage() - 1) * pageable.getRows());
		q.setMaxResults(pageable.getRows());

		if (values != null&&values.length>0) {
			for (int i = 0; i < values.length; i++) {
				q.setParameter(i, values[i]);
			}
		}
		pager.setRecords(q.list());
		pager.setTotalRows(this.countAll(hql, values));
		return pager;
	}

	@SuppressWarnings("unchecked")
	protected Page<E> findByHql(Pageable pageable, String hql,
			Map<String, Object> condition) {
		if (pageable == null) {
			throw new IllegalArgumentException("pageable 不能为空!");
		}
		Page<E> pager = new PageImpl<E>(pageable);

		Query q = this.getSession().createQuery(hql);
		q.setFirstResult((pageable.getPage() - 1) * pageable.getRows());
		q.setMaxResults(pageable.getRows());

		if (condition != null&&condition.size()>0) {
			q.setProperties(condition);
		}
		pager.setRecords(q.list());
		pager.setTotalRows(this.countAll(hql, condition));
		return pager;
	}

	protected Long countAll(String hql, Object... values) {
		if (hql == null) {
			return 0l;
		}
		String tmpHql = hql.toLowerCase();
//		String regex = hql.substring(0, tmpHql.indexOf("from"));
		//hql = hql.replaceFirst(regex, "select count(*) ");
		hql="select count(*) "+hql.substring(tmpHql.indexOf("from"));
		logger.debug("count(*) hql ---->" + hql);
		Query q = this.getSession().createQuery(hql);
		if (values != null) {
			for (int i = 0; i < values.length; i++) {
				q.setParameter(i, values[i]);
			}
		}
		return (Long) q.uniqueResult();
	}

	protected Long countAll(String hql, Map<String, Object> condition) {
		if (hql == null) {
			return 0l;
		}
		String tmpHql = hql.toLowerCase();
//		String regex = hql.substring(0, tmpHql.indexOf("from"));
		//hql = hql.replaceFirst(regex, "select count(*) ");
		hql="select count(*) "+hql.substring(tmpHql.indexOf("from"));
		logger.debug("count(*) hql ---->" + hql);
		Query q = this.getSession().createQuery(hql);
		if (condition != null&&condition.size()>0) {
			q.setProperties(condition);
		}
		return (Long) q.uniqueResult();
	}


	/**
	 * hql 查询，返回list of E
	 * 
	 * @param wheres
	 *            new String[]{"id=?","name=?"}
	 * @param single
	 *            object or param array list dictionary
	 * @return
	 */
	@SuppressWarnings("unchecked")
	protected List<E> executeQuery(String[] wheres, Object param) {
		StringBuffer hqlBuffer = new StringBuffer("FROM "
				+ this.entityClass.getName());
		hqlBuffer.append(HQHelper.where(wheres));
		Query query = this.getSession().createQuery(hqlBuffer.toString());
		setParameter(query, param);
		resumeIdx();
		List<E> list = query.list();
		return list;
	}

	@SuppressWarnings("rawtypes")
	protected List executeQuery(String hql, Object param, int start, int limit) {
		Query query = this.getSession().createQuery(hql);
		setParameter(query, param);
		resumeIdx();
		if (start != -1 || limit != -1) {
			query.setFirstResult(start);
			query.setMaxResults(limit);
		}
		List list = query.list();
		return list;
	}

	/**
	 * hql 查询,返回单个E
	 * 
	 * @param wheres
	 *            new String[]{"id=?","name=?"}
	 * @param single
	 *            object or param array list dictionary
	 * @return
	 */
	@SuppressWarnings("unchecked")
	protected E executeScalarQuery(String[] wheres, Object param) {
		StringBuffer hqlBuffer = new StringBuffer("FROM "
				+ this.entityClass.getName());
		hqlBuffer.append(HQHelper.where(wheres));
		Query query = this.getSession().createQuery(hqlBuffer.toString());
		setParameter(query, param);
		resumeIdx();
		E obj = (E) query.uniqueResult();
		return obj;
	}

	protected Object executeScalarQuery(String hql, Object param) {
		Query query = this.getSession().createQuery(hql);
		setParameter(query, param);
		resumeIdx();
		Object obj = query.uniqueResult();
		return obj;
	}

	/**
	 * sql查询
	 * 
	 * @param sql
	 *            sql statement
	 * @param param
	 *            single object or param array list dictionary
	 * @param persistCls
	 *            返回PO Class
	 * @return
	 */
	protected List<?> executeSQLQuery(String sql, Object param,
			Class<?>... persistCls) {
		return executeSQLQuery(sql, param, -1, -1, persistCls);
	}

	protected Object executeSQLScalarQuery(String sql, Object param,
			Class<?>... persistCls) {
		List<?> results = executeSQLQuery(sql, param, -1, -1, persistCls);
		if (results != null && results.size() > 0) {
			return results.get(0);
		}
		return null;
	}

	protected List<?> executeSQLQuery(String sql, Object param, int start,
			int limit, Class<?>... persistCls) {
		Query query = null;
		if (persistCls != null && persistCls.length != 0) {
			query = this.getSession().createSQLQuery(sql)
					.addEntity(persistCls[0]);
		} else {
			query = this.getSession().createSQLQuery(sql);
		}
		setParameter(query, param);
		resumeIdx();
		if (start != -1 || limit != -1) {
			query.setFirstResult(start);
			query.setMaxResults(limit);
		}
		return query.list();
	}

	protected List<?> executeSQLAliasQuery(String sql, Object param, int start,
			int limit, Class<?>... persistCls) {
		Query query = null;
		if (persistCls != null && persistCls.length != 0) {
			query = this.getSession().createSQLQuery(sql)
					.addEntity(persistCls[0]);
		} else {
			query = this.getSession().createSQLQuery(sql);
		}
		setParameter(query, param);
		resumeIdx();
		if (start != -1 || limit != -1) {
			query.setFirstResult(start);
			query.setMaxResults(limit);
		}
		return query.list();
	}

	protected List<?> executeSQLAliasMapQuery(String sql, Object param) {
		Query query = null;
		query = this.getSession().createSQLQuery(sql)
				.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
		setParameter(query, param);
		resumeIdx();
		return query.list();
	}

	@SuppressWarnings("unused")
	private void setParamArray(Query query, Object[] params) {
		if (params == null || params.length == 0) {
			return;
		}
		for (int i = 0; i < params.length; i++) {
			if (params[i] instanceof Collection) {// 如果是集合需要调用setParameterList方法设置参数
				query.setParameterList("list" + i, (Collection<?>) params[i]);
			} else {
				query.setParameter(i, params[i]);
			}
		}
	}

	private void setParameter(Query query, Object paramObj, Integer... index) {
		if (paramObj != null) {
			if (paramObj instanceof Collection) {
				Collection<?> params = (Collection<?>) paramObj;
				if (paramLevel != 0) {
					for (String paramName : query.getNamedParameters()) {
						if (paramName.equals("list" + listParamIdx)) {
							listParamIdx++;
							query.setParameterList(paramName, params);
							break;
						}
					}
				} else {
					Iterator<?> iter = params.iterator();
					int idx = 0;
					while (iter.hasNext()) {
						paramLevel++;
						setParameter(query, iter.next(), idx);
						paramLevel--;
						idx++;
					}
				}
			} else if (paramObj instanceof Dictionary) {
				Dictionary<?, ?> params = (Dictionary<?, ?>) paramObj;
				while (params.keys().hasMoreElements()) {
					String paramName = (String) params.keys().nextElement();
					Object paramValue = params.elements().nextElement();
					query.setParameter(paramName, paramValue);
				}
			} else if (paramObj instanceof Map) {
				Map<?, ?> params = (Map<?, ?>) paramObj;
				Iterator<?> iter = params.keySet().iterator();
				while (iter.hasNext()) {
					String paramName = (String) iter.next();
					Object paramValue = params.get(paramName);
					query.setParameter(paramName, paramValue);
				}
			} else if (paramObj instanceof String) {
				if (index != null && index.length != 0) {
					query.setString(index[0], (String) paramObj);
				} else {
					query.setString(0, (String) paramObj);
				}
			} else if (paramObj instanceof Integer
					|| paramObj.getClass() == int.class) {
				if (index != null && index.length != 0) {
					query.setInteger(index[0], (Integer) paramObj);
				} else {
					query.setInteger(0, (Integer) paramObj);
				}
			} else if (paramObj instanceof Float
					|| paramObj.getClass() == float.class) {
				if (index != null && index.length != 0) {
					query.setFloat(index[0], (Float) paramObj);
				} else {
					query.setFloat(0, (Float) paramObj);
				}
			} else if (paramObj instanceof Date
					|| paramObj.getClass() == Date.class) {
				if (index != null && index.length != 0) {
					query.setDate(index[0], (Date) paramObj);
				} else {
					query.setDate(0, (Date) paramObj);
				}
			} else if (paramObj instanceof BigDecimal) {
				if (index != null && index.length != 0) {
					query.setBigDecimal(index[0], (BigDecimal) paramObj);
				} else {
					query.setBigDecimal(0, (BigDecimal) paramObj);
				}
			} else if (paramObj instanceof Character) {
				if (index != null && index.length != 0) {
					query.setCharacter(index[0], (Character) paramObj);
				} else {
					query.setCharacter(0, (Character) paramObj);
				}
			} else if (paramObj.getClass().isArray()) {
				for (int i = 0; i < Array.getLength(paramObj); i++) {
					paramLevel++;
					setParameter(query, Array.get(paramObj, i), i);
					paramLevel--;
				}
			} else if (paramLevel != 0) {
				if (index != null && index.length != 0) {
					query.setParameter(index[0], paramObj);
				} else {
					query.setParameter(0, paramObj);
				}
			} else {
				throw new IllegalArgumentException(
						"Query Arguments invalidated,the parameter type of Query is not matched in ArrayList,HashMap,Hashtable,String,Integer,Date");
			}
		}
	}

	/**
	 * 重置Idx
	 */
	private void resumeIdx() {
		paramLevel = 0;
		listParamIdx = 0;
	}

	protected List<?> executeSQLMapQuery(String sql, Object param, int start,
			int limit) {
		Query query = null;
		query = this.getSession().createSQLQuery(sql)
				.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
		setParameter(query, param);
		resumeIdx();
		if (start != -1 || limit != -1) {
			query.setFirstResult(start);
			query.setMaxResults(limit);
		}
		return query.list();
	}

}
